// Test behavior of {logMessage: ... } test-only command.

const mongo = MongoRunner.runMongod({useLogFiles: true});
const admin = mongo.getDB('admin');

// Use our own log parser instead of `checkLog`
// because we want to force reading from a file.
// {getLog: 'global'} could mask output encoding issues.
// It would also be swell to get the record back as a return value.
function assertContainsLog(expectLog, severityCode) {
    const assertMsg = tojson(expectLog) + '(' + severityCode + ')';
    assert.soon(function() {
        // Search by splitting the log file on newlines,
        // filtering out blanks, parsing the remains as JSON,
        // then filtering on id, attr, and s.
        //
        // We should get precisely one match.
        // Too few and we didn't log,
        // too many and we didn't make the message unique enough.
        const splitFile = cat(mongo.fullOptions.logFile).split("\n").filter((l) => l != '');

        const mapped = [];

        for (const line in splitFile) {
            try {
                mapped.push(JSON.parse(splitFile[line]));
            } catch (e) {
                jsTest.log(`Failed to parse JSON: [${
                    e}] : complete unfiltered file contents as follows:\n ${
                    cat(mongo.fullOptions.logFile)}`);
                assert(false, `Failed to parse JSON for log line [${splitFile[line]}]`);
            }
        }

        const res = mapped
                        .filter((l) => (l.id === 5060500) && (l.s === severityCode) &&
                                    (0 === bsonWoCompare(l.attr, expectLog)))
                        .length;

        assert.lt(res, 2, "Repeated log entry: " + assertMsg);
        return res === 1;
    }, "Expected log not found: " + assertMsg, 20 * 1000);
}

const severities = {
    'severe': 'F',  // As in 'F'atal.
    'error': 'E',
    'warning': 'W',
    'info': 'I',
    'log': 'I',    // Not a typo.
    'debug': 'D',  // Actually D1-D5, overridden by main loop.
};

function test(msg, extra = undefined, severity = undefined, debug = undefined) {
    // Add entropy to msg to disambiguate multiple passes of the loop.
    if (severity !== undefined) {
        msg = severity + debug + ": " + msg;
    }

    const payload = {logMessage: msg};
    const expectLog = {msg: msg};
    if (extra !== undefined) {
        payload.extra = extra;
        expectLog.extra = extra;
    }

    let expectSeverity = severities['log'];
    if (severity !== undefined) {
        payload.severity = severity;
        if (severity === 'debug') {
            if (debug !== undefined) {
                payload.debugLevel = NumberInt(debug);
            }
            expectSeverity = 'D' + (debug ? debug : 1);
        } else {
            expectSeverity = severities[severity];
        }
    }

    assert.commandWorked(admin.runCommand(payload));
    assertContainsLog(expectLog, expectSeverity);
}

// Quick sanity check before diving deep.
test('Hello World');
test('Hello World', {arg: 'value'});

// Don't up the log level till now in order to avoid aggregious spammage.
admin.setLogLevel(5);

// Now roll up sleeves and check them all.
const testMessage = 'The quick "brown" \'fox\' jumped over,\nthe lazy\tdog.\u0000';
const extras = [
    undefined,
    {},
    {arg: 'value'},
    {arg: [{foo: 'bar'}], baz: 'qux'},
];

extras.forEach(function(extra) {
    Object.keys(severities).forEach(function(severity) {
        test(testMessage, extra, severity);
        if (severity === 'debug') {
            // Above call tests as default debug level,
            // Following loop tests at explicitly debug levels.
            for (let debug = 1; debug < 6; ++debug) {
                test(testMessage, extra, severity, debug);
            }
        }
    });
});

// Reset log level to avoid spammage during shutdown.
admin.setLogLevel(0);

MongoRunner.stopMongod(mongo);